return;
}
- $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) && $wgRequest->wasPosted();
+ $confirm = $wgRequest->getBool( 'wpConfirmProtect' ) &&
+ $wgRequest->wasPosted() &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
$moveonly = $wgRequest->getBool( 'wpMoveOnly' );
$reason = $wgRequest->getText( 'wpReasonProtect' );
* Output protection confirmation dialog
*/
function confirmProtect( $par, $reason, $limit = 'sysop' ) {
- global $wgOut;
+ global $wgOut, $wgUser;
wfDebug( "Article::confirmProtect\n" );
}
$confirm = htmlspecialchars( wfMsg( 'confirm' ) );
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "
<form id='protectconfirm' method='post' action=\"{$formaction}\">
</td>
</tr>
</table>
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
</form>\n" );
$wgOut->returnToMain( false );
function delete() {
global $wgUser, $wgOut, $wgMessageCache, $wgRequest;
$fname = 'Article::delete';
- $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted();
+ $confirm = $wgRequest->getBool( 'wpConfirm' ) &&
+ $wgRequest->wasPosted() &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
$reason = $wgRequest->getText( 'wpReason' );
# This code desperately needs to be totally rewritten
* Output deletion confirmation dialog
*/
function confirmDelete( $par, $reason ) {
- global $wgOut;
+ global $wgOut, $wgUser;
wfDebug( "Article::confirmDelete\n" );
$confirm = htmlspecialchars( wfMsg( 'confirm' ) );
$check = htmlspecialchars( wfMsg( 'confirmcheck' ) );
$delcom = htmlspecialchars( wfMsg( 'deletecomment' ) );
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "
<form id='deleteconfirm' method='post' action=\"{$formaction}\">
</td>
</tr>
</table>
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
</form>\n" );
$wgOut->returnToMain( false );
$wgOut->readOnlyPage( $this->getContent( true ) );
return;
}
+ if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ),
+ array( $this->mTitle->getPrefixedText(),
+ $wgRequest->getVal( 'from' ) ) ) ) {
+ $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
+ $wgOut->addWikiText( wfMsg( 'sessionfailure' ) );
+ return;
+ }
$dbw =& wfGetDB( DB_MASTER );
# Enhanced rollback, marks edits rc_bot=1
'target=' . urlencode($this->mNewUser) );
if ( !$this->mNewid && $wgUser->isAllowed('rollback') ) {
$rollback = ' <strong>[' . $sk->makeKnownLinkObj( $wgTitle, wfMsg( 'rollbacklink' ),
- 'action=rollback&from=' . urlencode($this->mNewUser) ) . ']</strong>';
+ 'action=rollback&from=' . urlencode($this->mNewUser) .
+ '&token=' . urlencode( $wgUser->editToken( array( $wgTitle->getPrefixedText(), $this->mNewUser ) ) ) ) .
+ ']</strong>';
} else {
$rollback = '';
}
# Deleting old images doesn't require confirmation
if ( !is_null( $oldimage ) || $confirm ) {
- $this->doDelete();
+ if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
+ $this->doDelete();
+ } else {
+ $wgOut->fatalError( wfMsg( 'sessionfailure' ) );
+ }
return;
}
$fname = 'ImagePage::doDelete';
$reason = $wgRequest->getVal( 'wpReason' );
- $image = $wgRequest->getVal( 'image' );
$oldimage = $wgRequest->getVal( 'oldimage' );
$dbw =& wfGetDB( DB_MASTER );
if ( !is_null( $oldimage ) ) {
+ if ( strlen( $oldimage ) < 16 ) {
+ $wgOut->unexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
+ return;
+ }
+ if ( strstr( $oldimage, "/" ) || strstr( $oldimage, "\\" ) ) {
+ $wgOut->unexpectedValueError( 'oldimage', htmlspecialchars($oldimage) );
+ return;
+ }
# Squid purging
if ( $wgUseSquid ) {
$urlArr = Array(
$dbw->delete( 'oldimage', array( 'oi_archive_name' => $oldimage ) );
$deleted = $oldimage;
} else {
- if ( is_null ( $image ) ) {
- $image = $this->mTitle->getDBkey();
- }
+ $image = $this->mTitle->getDBkey();
$dest = wfImageDir( $image );
$archive = wfImageDir( $image );
function revert()
{
- global $wgOut, $wgRequest;
+ global $wgOut, $wgRequest, $wgUser;
global $wgUseSquid, $wgInternalServer, $wgDeferredUpdateList;
$oldimage = $wgRequest->getText( 'oldimage' );
$wgOut->sysopRequired();
return;
}
+ if( !$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $oldimage ) ) {
+ $wgOut->errorpage( 'internalerror', 'sessionfailure' );
+ return;
+ }
$name = substr( $oldimage, 15 );
$dest = wfImageDir( $name );
} else {
$url = htmlspecialchars( wfImageArchiveUrl( $img ) );
if( $wgUser->getID() != 0 && $wgTitle->userCanEdit() ) {
+ $token = urlencode( $wgUser->editToken( $img ) );
$rlink = $this->skin->makeKnownLink( $wgTitle->getPrefixedText(),
wfMsg( 'revertimg' ), 'action=revert&oldimage=' .
- urlencode( $img ) );
+ urlencode( $img ) . "&wpEditToken=$token" );
$dlink = $this->skin->makeKnownLink( $wgTitle->getPrefixedText(),
- $del, 'action=delete&oldimage=' . urlencode( $img ) );
+ $del, 'action=delete&oldimage=' . urlencode( $img ) .
+ "&wpEditToken=$token" );
} else {
# Having live active links for non-logged in users
# means that bots and spiders crawling our site can
$url = $img->getViewURL();
#$label = htmlspecialchars( $label );
- $alt = preg_replace( '/<[^>]*>/', '', $label);
- $alt = preg_replace('/&(?!:amp;|#[Xx][0-9A-fa-f]+;|#[0-9]+;|[a-zA-Z0-9]+;)/', '&', $alt);
- $alt = str_replace( array('<', '>', '"'), array('<', '>', '"'), $alt );
+ $alt = Sanitizer::stripAllTags( $label );
$width = $height = 0;
if ( $img->exists() )
return $this->makeMediaLinkObj( $nt, $alt );
}
- /** @todo document */
- function makeMediaLinkObj( $nt, $alt = '', $nourl=false ) {
- if ( ! isset( $nt ) )
- {
+ /**
+ * Create a direct link to a given uploaded file.
+ *
+ * @param Title $title
+ * @param string $text pre-sanitized HTML
+ * @param bool $nourl Mask absolute URLs, so the parser doesn't
+ * linkify them (it is currently not context-aware)
+ * @return string HTML
+ *
+ * @access public
+ * @todo Handle invalid or missing images better.
+ */
+ function makeMediaLinkObj( $title, $text = '', $nourl=false ) {
+ if( is_null( $title ) ) {
### HOTFIX. Instead of breaking, return empty string.
- $s = $alt;
+ return $text;
} else {
- $name = $nt->getDBKey();
- $img = Image::newFromTitle( $nt );
- $url = $img->getURL();
- # $nourl can be set by the parser
- # this is a hack to mask absolute URLs, so the parser doesn't
- # linkify them (it is currently not context-aware)
- # 2004-10-25
- if ($nourl) { $url=str_replace("http://","http-noparse://",$url) ; }
- if ( empty( $alt ) ) {
- $alt = preg_replace( '/\.(.+?)^/', '', $name );
+ $name = $title->getDBKey();
+ $img = Image::newFromTitle( $title );
+ $url = $img->getURL();
+ if( $nourl ) {
+ $url = str_replace( "http://", "http-noparse://", $url );
+ }
+ $alt = htmlspecialchars( $title->getText() );
+ if( $text == '' ) {
+ $text = $alt;
}
$u = htmlspecialchars( $url );
- $s = "<a href=\"{$u}\" class='internal' title=\"{$alt}\">{$alt}</a>";
+ return "<a href=\"{$u}\" class='internal' title=\"{$alt}\">{$text}</a>";
}
- return $s;
}
/** @todo document */
* parameter level defines if we are on an indentation level
*/
function tocLine( $anchor, $tocline, $tocnumber, $level ) {
- return "\n<li class='toclevel-$level'><a href=\"#" . $anchor . '"><span class="tocnumber">' . $tocnumber . '</span> <span class="toctext">' . $tocline . '</span></a>';
+ return "\n<li class='toclevel-$level'><a href=\"#" .
+ $anchor . '"><span class="tocnumber">' .
+ $tocnumber . '</span> <span class="toctext">' .
+ $tocline . '</span></a>';
}
/** @todo document */
$ipb = new IPBlockForm();
$action = $wgRequest->getVal( 'action' );
- if ( 'success' == $action ) { $ipb->showSuccess(); }
- else if ( $wgRequest->wasPosted() && 'submit' == $action ) { $ipb->doSubmit(); }
- else { $ipb->showForm( '' ); }
+ if ( 'success' == $action ) {
+ $ipb->showSuccess();
+ } else if ( $wgRequest->wasPosted() && 'submit' == $action &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+ $ipb->doSubmit();
+ } else {
+ $ipb->showForm( '' );
+ }
}
/**
$scBlockAddress = htmlspecialchars( $this->BlockAddress );
$scBlockExpiry = htmlspecialchars( $this->BlockExpiry );
$scBlockReason = htmlspecialchars( $this->BlockReason );
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "
<form id=\"blockip\" method=\"post\" action=\"{$action}\">
</td>
</tr>
</table>
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
</form>\n" );
}
if( $wgUser->isAllowed('rollback') ) {
$extraRollback = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
+ $extraRollback .= '&token=' . urlencode(
+ $wgUser->editToken( array( $page->getPrefixedText(), $target ) ) );
# $target = $wgRequest->getText( 'target' );
$topmarktext .= ' ['. $sk->makeKnownLinkObj( $page,
$messages['rollbacklink'],
$f = new EmailUserForm( $nu->getName() . " <{$address}>", $target );
- if ( "success" == $action ) { $f->showSuccess(); }
- else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
- else { $f->showForm(); }
+ if ( "success" == $action ) {
+ $f->showSuccess();
+ } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+ $f->doSubmit();
+ } else {
+ $f->showForm();
+ }
}
/**
$titleObj = Title::makeTitle( NS_SPECIAL, "Emailuser" );
$action = $titleObj->escapeLocalURL( "target=" .
urlencode( $this->target ) . "&action=submit" );
+ $token = $wgUser->editToken();
$wgOut->addHTML( "
<form id=\"emailuser\" method=\"post\" action=\"{$action}\">
<td> </td><td align='left'>
<input type='submit' name=\"wpSend\" value=\"{$ems}\" />
</td></tr></table>
+<input type='hidden' name='wpEditToken' value=\"$token\" />
</form>\n" );
}
if ( "success" == $action ) {
$msg = wfMsg( "ipusuccess", htmlspecialchars( $ip ) );
$ipu->showList( $msg );
- } else if ( "submit" == $action && $wgRequest->wasPosted() ) {
+ } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
if ( ! $wgUser->isAllowed('block') ) {
$wgOut->sysopRequired();
return;
$wgOut->setSubtitle( wfMsg( "formerror" ) );
$wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
}
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "
<form id=\"unblockip\" method=\"post\" action=\"{$action}\">
</td>
</tr>
</table>
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
</form>\n" );
}
$action = $wgRequest->getVal( 'action' );
$f = new DBLockForm();
- if ( "success" == $action ) { $f->showSuccess(); }
- else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
- else { $f->showForm( "" ); }
+ if ( "success" == $action ) {
+ $f->showSuccess();
+ } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+ $f->doSubmit();
+ } else {
+ $f->showForm( "" );
+ }
}
/**
$elr = htmlspecialchars( wfMsg( "enterlockreason" ) );
$titleObj = Title::makeTitle( NS_SPECIAL, "Lockdb" );
$action = $titleObj->escapeLocalURL( "action=submit" );
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( <<<END
<form id="lockdb" method="post" action="{$action}">
</td>
</tr>
</table>
+<input type="hidden" name="wpEditToken" value="{$token}" />
</form>
END
);
$f = new MovePageForm();
- if ( 'success' == $action ) { $f->showSuccess(); }
- else if ( 'submit' == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
- else { $f->showForm( '' ); }
+ if ( 'success' == $action ) {
+ $f->showSuccess();
+ } else if ( 'submit' == $action && $wgRequest->wasPosted()
+ && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+ $f->doSubmit();
+ } else {
+ $f->showForm( '' );
+ }
}
/**
$titleObj = Title::makeTitle( NS_SPECIAL, 'Movepage' );
$action = $titleObj->escapeLocalURL( 'action=submit' );
+ $token = htmlspecialchars( $wgUser->editToken() );
if ( $err != '' ) {
$wgOut->setSubtitle( wfMsg( 'formerror' ) );
</td>
</tr>
</table>
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
</form>\n" );
}
* Load some values
*/
function PreferencesForm( &$request ) {
- global $wgLang, $wgContLang, $wgAllowRealName;
+ global $wgLang, $wgContLang, $wgUser, $wgAllowRealName;
$this->mQuickbar = $request->getVal( 'wpQuickbar' );
$this->mOldpass = $request->getVal( 'wpOldpass' );
$this->mAction = $request->getVal( 'action' );
$this->mReset = $request->getCheck( 'wpReset' );
$this->mPosted = $request->wasPosted();
- $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) && $this->mPosted;
+ $this->mSaveprefs = $request->getCheck( 'wpSaveprefs' ) &&
+ $this->mPosted &&
+ $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
# User toggles (the big ugly unsorted list of checkboxes)
$this->mToggles = array();
}
$wgOut->addHTML( "</fieldset>\n\n" );
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( "
<div id='prefsubmit'>
<div>
</div>
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
</form>\n" );
}
}
var $mTargetTimestamp;
function UndeleteForm( &$request, $par = "" ) {
+ global $wgUser;
$this->mAction = $request->getText( 'action' );
$this->mTarget = $request->getText( 'target' );
$this->mTimestamp = $request->getText( 'timestamp' );
- $this->mRestore = $request->getCheck( 'restore' ) && $request->wasPosted();
+ $this->mRestore = $request->getCheck( 'restore' ) &&
+ $request->wasPosted() &&
+ $wgUser->matchEditToken( $request->getVal( 'wpEditToken' ) );
if( $par != "" ) {
$this->mTarget = $par;
}
$action = $titleObj->escapeLocalURL( "action=submit" );
$encTarget = htmlspecialchars( $this->mTarget );
$button = htmlspecialchars( wfMsg("undeletebtn") );
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML("
<form id=\"undelete\" method=\"post\" action=\"{$action}\">
<input type=\"hidden\" name=\"target\" value=\"{$encTarget}\" />
<input type=\"submit\" name=\"restore\" value=\"{$button}\" />
+ <input type='hidden' name='wpEditToken' value=\"{$token}\" />
");
# Show relevant lines from the deletion log:
$action = $wgRequest->getVal( 'action' );
$f = new DBUnlockForm();
- if ( "success" == $action ) { $f->showSuccess(); }
- else if ( "submit" == $action && $wgRequest->wasPosted() ) { $f->doSubmit(); }
- else { $f->showForm( "" ); }
+ if ( "success" == $action ) {
+ $f->showSuccess();
+ } else if ( "submit" == $action && $wgRequest->wasPosted() &&
+ $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
+ $f->doSubmit();
+ } else {
+ $f->showForm( "" );
+ }
}
/**
$lb = htmlspecialchars( wfMsg( "unlockbtn" ) );
$titleObj = Title::makeTitle( NS_SPECIAL, "Unlockdb" );
$action = $titleObj->escapeLocalURL( "action=submit" );
+ $token = htmlspecialchars( $wgUser->editToken() );
$wgOut->addHTML( <<<END
</td>
</tr>
</table>
+<input type="hidden" name="wpEditToken" value="{$token}" />
</form>
END
);
* @return bool
*/
function verify( $tmpfile, $extension ) {
- if( $this->triggersIEbug( $tmpfile ) ) {
+ if( $this->triggersIEbug( $tmpfile ) ||
+ $this->triggersSafariBug( $tmpfile ) ) {
return false;
}
*/
function triggersIEbug( $filename ) {
$file = fopen( $filename, 'rb' );
- $chunk = strtolower( fread( $file, 200 ) );
+ $chunk = strtolower( fread( $file, 256 ) );
fclose( $file );
- $tags = array( '<html', '<head', '<body', '<script' );
+ $tags = array(
+ '<body',
+ '<head',
+ '<html',
+ '<img',
+ '<pre',
+ '<script',
+ '<table',
+ '<title' );
foreach( $tags as $tag ) {
if( false !== strpos( $chunk, $tag ) ) {
return true;
}
return false;
}
+
+ /**
+ * Apple's Safari browser performs some unsafe file type autodetection
+ * which can cause legitimate files to be interpreted as HTML if the
+ * web server is not correctly configured to send the right content-type
+ * (or if you're really uploading plain text and octet streams!)
+ *
+ * Returns true if Safari would mistake the given file for HTML
+ * when served with a generic content-type.
+ *
+ * @param string $filename
+ * @return bool
+ */
+ function triggersSafariBug( $filename ) {
+ $file = fopen( $filename, 'rb' );
+ $chunk = strtolower( fread( $file, 1024 ) );
+ fclose( $file );
+
+ $tags = array(
+ '<html',
+ '<script',
+ '<title' );
+ foreach( $tags as $tag ) {
+ if( false !== strpos( $chunk, $tag ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
}
?>
* login credentials aren't being hijacked with a foreign form
* submission.
*
+ * @param mixed $salt - Optional function-specific data for hash.
+ * Use a string or an array of strings.
* @return string
* @access public
*/
- function editToken() {
+ function editToken( $salt = '' ) {
if( !isset( $_SESSION['wsEditToken'] ) ) {
$token = dechex( mt_rand() ) . dechex( mt_rand() );
$_SESSION['wsEditToken'] = $token;
+ } else {
+ $token = $_SESSION['wsEditToken'];
+ }
+ if( is_array( $salt ) ) {
+ $salt = implode( '|', $salt );
}
- return $_SESSION['wsEditToken'];
+ return md5( $token . $salt );
}
/**
* user's own login session, not a form submission from a third-party
* site.
*
- * @param string $val
+ * @param string $val - the input value to compare
+ * @param string $salt - Optional function-specific data for hash
* @return bool
* @access public
*/
- function matchEditToken( $val ) {
- if( !isset( $_SESSION['wsEditToken'] ) )
- return false;
- return $_SESSION['wsEditToken'] == $val;
+ function matchEditToken( $val, $salt = '' ) {
+ return ( $val == $this->editToken( $salt ) );
}
}
# only shown if there is an edit comment
'editcomment' => "The edit comment was: \"<i>$1</i>\".",
'revertpage' => "Reverted edit of $2, changed back to last version by $1",
+'sessionfailure' => 'There seems to be a problem with your login session;
+this action has been canceled as a precaution against session hijacking.
+Please hit "back" and reload the page you came from, then try again.',
'protectlogpage' => 'Protection_log',
'protectlogtext' => "Below is a list of page locks/unlocks.
See [[Project:Protected page]] for more information.",